#ifndef SDUNIVERSE_H
#define SDUNIVERSE_H

#include <vector>
#include <set>
#include <math.h>
#include <map>
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include "../src/chipmunk.h"
#include "sdInput.h"
#include "sdObject.h"
#include "sdPlayer.h"
#include "sdBad.h"
#include "Client.h"

class sdUniverse {
    private:
        int ticks_;
        int height_;
        int width_;
        sdInput input_;
        cpSpace* space_;
        sdPlayer player_;
        sdPlayer netPlayer_;
        static bool killPlayer_;
        Client *client_;
        int ip1_;
        int ip2_;
        int ip3_;
        int ip4_;
        bool isDefault_;

        //
        cpFloat nextSpawnTime_;
        cpFloat timeToSpawn_;
        cpFloat badVel;
        cpFloat badTurn;
        static int kills;

        //std::vector<sdObject*> objectList_;
        //TODO: CHANGE TO OBJECT
        cpBody* statics_;

        static sdUniverse* this_;

        //Change to STL contaner
        //TODO: MAKE UNSTATIC
        static std::map<cpShape*, sdObject*> objects_;
        static std::set<sdObject*> doomed; //TODO: STL CONTAINER ONLY CONTAINING "DEAD" OBJECTS
        static std::set<sdTimed*> timed_; //Stl container containing objects to update
        static std::set<sdBad*> bad_;
        //std::vector<sdObject&> updateme;

        static int sdCollisionBW(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
        static int sdCollisionSW(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
        static int sdCollisionBS(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
        static int sdCollisionBE(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
        static int sdCollisionSE(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);

        void addObstical(int numVerts, cpVect* verts, cpVect offset);

        static sdUniverse* getUniverse() {return this_;}
    public:
        sdUniverse(int height, int width)
            : ticks_(0), height_(height), width_(width), input_(height, width), space_(cpSpaceNew()), player_(), netPlayer_(), isDefault_(true){
                this_ = this;
                init();

                nextSpawnTime_ = 2.0;
                timeToSpawn_ = nextSpawnTime_;
                badVel = 5000.0;
                badTurn = 1.0;
                kills = 0;
                killPlayer_ = false;
            } 
        sdUniverse(int height, int width, int a1, int a2, int a3, int a4)
            : ticks_(0), height_(height), width_(width), input_(height, width), space_(cpSpaceNew()), player_(), netPlayer_(), isDefault_(false), ip1_(a1), ip2_(a2), ip3_(a3), ip4_(a4) {
                this_ = this;
                init();

                nextSpawnTime_ = 2.0;
                timeToSpawn_ = nextSpawnTime_;
                badVel = 5000.0;
                badTurn = 1.0;

                kills = 0;
                killPlayer_ = false;
                isDefault_ = false;
            }

        cpSpace* getSpace() {return space_;}
        void init();
        void update();
        void updateObjects(cpFloat ticks);
        void moveBads();

        static std::map<cpShape*, sdObject*>& getObjects() {
            return objects_;
        }

        void updateThis(sdTimed& timed) {timed_.insert(&timed);}

        void doom(sdObject& o);
        void kill();
        void insert(sdObject& o);

        void addBad(int hp, cpFloat velocity, cpFloat turn, cpVect pos);
        cpVect randomLoc() {
            cpVect result;

            do {
                result = cpv((rand() % height_) - height_ / 2, (rand() % width_) - width_ / 2);
            } while(abs(result.x - player_.getShip().getPos().x) < 200 && abs(result.y - player_.getShip().getPos().y) < 200);

            return result;
        }

        int collision(cpShape *a,
                cpShape *b,
                cpContact *contacts,
                int numContacts,
                cpFloat normal_coef,
                void *data) {
            //std::cout << "Colliding Key[" << a << "] Key[" << b << "]";
            //std::cout << " Value[" << objects_[a] << "] Value[" << objects_[b] << "]" << std::endl;
            objects_[a]->collide(NULL);
            objects_[b]->collide(NULL);
            //printf("colliding objects: %p  vs  %p\n", objects[a], objects[b]);
            return 1;
        }

        //players
        inline sdPlayer& getPlayer() {return player_;}    
        inline sdPlayer& getNetPlayer() { return netPlayer_; }

        //input
        inline sdInput& getInputClass() {return input_;}

        static int collisionCb(cpShape *a,
                cpShape *b,
                cpContact *contacts,
                int numContacts,
                cpFloat normal_coef,
                void *data) {
            return sdUniverse::getUniverse()->collision(a, b, contacts, numContacts, normal_coef, data);
        }
};

#endif
